#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "qcustomplot.h"
#include "muParser/muParser.h"
#include "tspline.h"

using namespace mu;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

   // ui->pushButton_2->setHidden(true);

    table = new QTableWidget(0);
    table->setColumnCount(5);
    table->setWindowTitle("Main task");
    QStringList headers;
    headers.append("i");
    headers.append("x_i");
    headers.append("v_i");
    headers.append("v2_i");
    headers.append("|v_i-v2_i|");
    table->setHorizontalHeaderLabels(headers);
    table->verticalHeader()->hide();
    table->setColumnWidth(0,35);

    splineTable = new QTableWidget(0);
    splineTable->setColumnCount(5);
    splineTable->setWindowTitle("Test tasks");
    headers.clear();
    headers.append("i");
    headers.append("x_i");
    headers.append("u_i");
    headers.append("v_i");
    headers.append("|u_i-v_i|");
    splineTable->setHorizontalHeaderLabels(headers);
    splineTable->verticalHeader()->hide();
    splineTable->setColumnWidth(0,35);

    difference = new QCustomPlot();
    difference->show();
}

MainWindow::~MainWindow()
{
    delete table;
    delete splineTable;
    delete ui;
}

bool MainWindow::event(QEvent *mEvent)
{
    if (mEvent->type()==QEvent::Close)
    {
        table->close();
        splineTable->close();
        difference->close();
        this->close();
        return true;
    }
    return QMainWindow::event(mEvent);
}

void MainWindow::countFunc(QString function, double x0, double xn, int n,  QVector<double>& x,  QVector<double>& y, QVector<double>& yDiff, QVector<double>& ySecondDiff)
{
    double epsForDiff = 1e-6;
    x.clear();
    yDiff.clear();
    ySecondDiff.clear();
    y.clear();
    x.resize(n+1);
    y.resize(n+1);
    yDiff.resize(n+1);
    ySecondDiff.resize(n+1);
    double h = (xn-x0) / n;

    Parser p;
    double variable;
    p.DefineVar("x", &variable);
    p.SetExpr(function.toStdString().c_str());

    x[0]=x0;
    variable=x[0];
    y[0] = p.Eval();
    yDiff[0] = p.Diff(&variable, variable, epsForDiff);
    ySecondDiff[0] = p.SecondDiff(&variable, variable, epsForDiff);

    for (int i=1; i<n+1; ++i)
    {
      x[i] = variable = x[i-1]+h;
      variable=x[i];
      y[i] = p.Eval();
      yDiff[i] = p.Diff(&variable, variable, epsForDiff);
      ySecondDiff[i] = p.SecondDiff(&variable, variable, epsForDiff);

    }
}

void MainWindow::plotFunc(QVector<double>& x, QVector<double>& y, QPen color)
{
    ui->graphWidget->addGraph();

    ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->setData(x, y);
    // give the axes some labels:
    ui->graphWidget->xAxis->setLabel("x");
    ui->graphWidget->yAxis->setLabel("y");
    ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->setPen(color);
    //ui->graphWidget->xAxis->setRange(x[0]-0.5,x[x.size()+1]+0.5);
    //ui->graphWidget->yAxis->setRange(-2.5,2.5);
    //ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->rescaleAxes(true);
    //ui->graphWidget->replot();
}

void MainWindow::plotFunc2(QVector<double>& x, QVector<double>& y, QPen color)
{
    difference->clearGraphs();
    difference->addGraph();

    difference->graph(0)->setData(x, y);
    // give the axes some labels:
    difference->xAxis->setLabel("x");
    difference->yAxis->setLabel("y");
    difference->graph(0)->setPen(color);
    //ui->graphWidget->xAxis->setRange(x[0]-0.5,x[x.size()+1]+0.5);
    //ui->graphWidget->yAxis->setRange(-2.5,2.5);
    //ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->rescaleAxes(true);
    difference->graph(0)->rescaleAxes();
    difference->replot();
}

double funcU(double x)
{
    double ksi = 0.125;
    double c1,c2,c3,c4;
    //Для ГУ 0-1
//    c1 = -.106214632395716977;
//    c2 = -.870718455337776241;
//    c3 = 0.178167456996921574;
//    c4 = -.221053579437323604;

    //Для ГУ 1-0
    c1 = -.710983218969850030;
    c2 = .734050131236356810;
    c3 = -0.404587899968865180e-1;
    c4 = 1.03814167538536908;
    if (x<ksi)
    {
        double B = sqrt((ksi*ksi+1)/(ksi+1));
        return c1*exp(B*x)+c2*exp(-B*x)+cos(ksi)/(ksi*ksi+1);
        //return cos(ksi)/(ksi+1)*(1-exp(B*x))+c1*(exp(-B*x)-exp(B*x));
    }
    else
    {
        double A = sqrt((ksi*ksi+3)/(ksi*ksi+1));
        return c3*exp(A*x)+c4*exp(-A*x)+sin(ksi)/(ksi*ksi+3);
        //return sin(ksi)/(ksi*ksi+3)*(1-exp(A*(x-1)))+exp(A*(x-1))+ c2*(exp(-A*x)-exp(A*(x-2)));
    }
}


typedef double func(double);

void findV(QVector<double>& x, QVector<double>& v, int n, func* k, func* q, func* f)
{
    double S0=1;
    double Sn=0;
    double h=1.0/n;
    double ksi=0.125;

    double* d = new double [n+1];
    double* a = new double [n+1];
    double* fi = new double [n+1];

    //x.clear();
    //x.resize(n+1);
    v.clear();
    v.resize(n+1);
    //x[0]=0;
//    for (int i=1; i<n+1; ++i)
//    {
//      x[i] = x[i-1]+h;
//    }

    for (int i=1; i<=n-1; i++)
    {

        d[i] = q(x[i]);
        fi[i]=f(x[i]);
        a[i]=k((x[i-1]+x[i])/2.0);

        if ((x[i]-h/2 < ksi) && (x[i]+h/2 > ksi))
        {
            d[i] = 1/h *( (ksi-x[i]+h/2)*q((x[i]-h/2+ksi)/2) +
                          (-ksi+x[i]+h/2)*q((x[i]+h/2+ksi)/2));
            fi[i] = 1/h *( (ksi-x[i]+h/2)*f((x[i]-h/2+ksi)/2) +
                          (-ksi+x[i]+h/2)*f((x[i]+h/2+ksi)/2));
        }
        if ((x[i-1] < ksi) && (x[i] > ksi))
        {
            a[i] = h/ (1/k((x[i-1]+ksi)/2)*(ksi-x[i-1]) + 1/k((x[i]+ksi)/2)*(x[i]-ksi));
        }
    }
    a[n]=k((x[n-1]+x[n])/2.0);

    double* alpha = new double [n+1];
    double* beta = new double [n+1];
    memset(alpha,0,(n+1)*sizeof(double));
    memset(beta,0,(n+1)*sizeof(double));

    alpha[1]=0.0;
    beta[1]=S0;
    v[0]=S0;
    v[n]=Sn;

    for (int i=1; i<n; i++)
    {
        double Bi = a[i+1]/(h*h);
        double Ai = a[i]/(h*h);
        double Ci = (a[i]+a[i+1])/(h*h) + d[i];
        alpha[i+1] = Bi/(Ci-Ai*alpha[i]);
        beta[i+1] = (fi[i]+Ai*beta[i])/(Ci-Ai*alpha[i]);
    }

    for (int i=n-1; i>=0; i--)
    {
        v[i]=alpha[i+1]*v[i+1]+beta[i+1];
    }

    delete[] alpha;
    delete[] beta;
    delete[] d;
    delete[] a;
    delete[] fi;
}

double k(double x)
{
    double ksi=0.125;
    return x<ksi ? ksi+1 : ksi*ksi+1;
}
double q(double x)
{
    double ksi=0.125;
    return x<ksi ? ksi*ksi+1 : ksi*ksi+3;
}
double f(double x)
{
    double ksi=0.125;
    return x<ksi ? cos(ksi) : sin(ksi);
}

double k1(double x)
{
    double ksi=0.125;
    //return x+1;
    return x<ksi ? x+1 : x*x+1;
}
double q1(double x)
{
    double ksi=0.125;
    //return x*x+1;
    return x<ksi ? x*x+1 : x*x+3;
}
double f1(double x)
{
    double ksi=0.125;
    //return cos(x);
    return x<ksi ? cos(x) : sin(x);
}

void MainWindow::on_pushButton_clicked()
{    
    ui->graphWidget->clearGraphs();

    int n = ui->spinBox->value();
    double h = 1.0/n;

    QVector<double> x(n+1), v(n+1), u(n+1);
    x[0]=0;
    u[0]=funcU(x[0]);
    for (int i=1; i<n+1; ++i)
    {
      x[i] = i*h;
      u[i]=funcU(x[i]);
    }
    findV(x,v,n, k,q,f);

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        splineTable->setRowCount(n+1);
        for (int i = 0; i<n+1; i++)
        {
            splineTable->setItem(i,0,new QTableWidgetItem(QString::number(i)));
            splineTable->setItem(i,1,new QTableWidgetItem(QString::number(x[i])));
            splineTable->setItem(i,2,new QTableWidgetItem(QString::number(u[i])));
            splineTable->setItem(i,3,new QTableWidgetItem(QString::number(v[i])));
            splineTable->setItem(i,4,new QTableWidgetItem(QString::number(fabs(u[i]-v[i]))));
        }
        splineTable->show();
    }

    double maxDiff=-1.0;
    int index = -1;
    for (int i=0; i<n; i++)
    {
        if (fabs(u[i]-v[i])>maxDiff)
        {
            maxDiff = fabs(u[i]-v[i]);
            index = i;
        }
    }
    ui->label_Dev->setText(QString::number(maxDiff));
    ui->label_Dev_2->setText(QString::number(index));

    QVector<double> diff(n+1);
    for (int i=0; i<n+1; i++)
        diff[i]=fabs(u[i]-v[i]);

    plotFunc(x,u);
    plotFunc(x,v, QPen(Qt::red));
    plotFunc2(x,diff, QPen(Qt::green));
    ui->graphWidget->graph(0)->rescaleAxes();
    //ui->graphWidget->graph(1)->rescaleAxes(true);
    ui->graphWidget->replot();
}

void MainWindow::on_pushButton_2_clicked()
{    
    ui->graphWidget->clearGraphs();

    int n = ui->spinBox->value();
    double h = 1.0/n;

    QVector<double> x(n+1), v(n+1), u(n+1);
    x[0]=0;
    for (int i=1; i<n+1; ++i)
    {
      x[i] = i*h;
      u[i]=x[i]*x[i];
    }

    double* alpha = new double [n+1];
    double* beta = new double [n+1];
    //double* v = new double [n+1];
    //memset(v,0,(n+1)*sizeof(double));
    memset(alpha,0,(n+1)*sizeof(double));
    memset(beta,0,(n+1)*sizeof(double));

    alpha[1]=0.0;
    beta[1]=0;
    v[n]=1;

    for (int i=1; i<n; i++)
    {
        alpha[i+1] = 1.0/(2-alpha[i]);
        beta[i+1] = (-2*h*h+beta[i])/(2-alpha[i]);
    }

    for (int i=n-1; i>=0; i--)
    {
        v[i]=alpha[i+1]*v[i+1]+beta[i+1];
    }

    double maxDiff=-1.0;
    int index = -1;
    for (int i=0; i<n; i++)
    {
        if (fabs(u[i]-v[i])>maxDiff)
        {
            maxDiff = fabs(u[i]-v[i]);
            index = i;
        }
    }
    ui->label_Dev->setText(QString::number(maxDiff));
    ui->label_Dev_2->setText(QString::number(index));

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        splineTable->setRowCount(n+1);
        for (int i = 0; i<n+1; i++)
        {
            splineTable->setItem(i,0,new QTableWidgetItem(QString::number(i)));
            splineTable->setItem(i,1,new QTableWidgetItem(QString::number(x[i])));
            splineTable->setItem(i,2,new QTableWidgetItem(QString::number(u[i])));
            splineTable->setItem(i,3,new QTableWidgetItem(QString::number(v[i])));
            splineTable->setItem(i,4,new QTableWidgetItem(QString::number(fabs(u[i]-v[i]))));
        }
        splineTable->show();
    }

    QVector<double> diff(n+1);
    for (int i=0; i<n+1; i++)
        diff[i]=fabs(u[i]-v[i]);

    plotFunc(x,u);
    ui->graphWidget->graph(0)->rescaleAxes();
    //ui->graphWidget->xAxis->setRange(x[0]-0.1,x[n]);


    plotFunc(x, v,QPen(Qt::red));

    plotFunc2(x,diff, QPen(Qt::green));
    ui->graphWidget->graph(1)->rescaleAxes(true);
    ui->graphWidget->replot();

}

void MainWindow::on_pushButton_3_clicked()
{
    ui->graphWidget->clearGraphs();

    int n = ui->spinBox->value();
    double h = 1.0/n;

    QVector<double> x(n+1), v(n+1), v2(n+1);
    x[0]=0;
    for (int i=1; i<n+1; ++i)
    {
      x[i] = i*h;
    }
    findV(x,v,n, k1,q1,f1);

    int n2 = n*2;
    double h2 = 1.0/n2;
    QVector<double> x2(n2+1);
    x2[0]=0;
    for (int i=1; i<n2+1; ++i)
    {
       x2[i] = i*h2;
    }
    findV(x2,v2,n2, k1,q1,f1);

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        table->setRowCount(n+1);
        for (int i = 0; i<n+1; i++)
        {
            table->setItem(i,0,new QTableWidgetItem(QString::number(i)));
            table->setItem(i,1,new QTableWidgetItem(QString::number(x[i])));
            table->setItem(i,2,new QTableWidgetItem(QString::number(v[i])));
            table->setItem(i,3,new QTableWidgetItem(QString::number(v2[2*i])));
            table->setItem(i,4,new QTableWidgetItem(QString::number(fabs(v2[2*i]-v[i]))));
        }
        table->show();
    }

    double maxDiff=-1.0;
    int index = -1;
    for (int i=0; i<n; i++)
    {
        if (fabs(v2[2*i]-v[i])>maxDiff)
        {
            maxDiff = fabs(v2[2*i]-v[i]);
            index = i;
        }
    }
    ui->label_Dev->setText(QString::number(maxDiff));
    ui->label_Dev_2->setText(QString::number(index));

    QVector<double> diff(n+1);
    for (int i=0; i<n+1; i++)
        diff[i]=fabs(v2[i*2]-v[i]);
    plotFunc2(x,diff, QPen(Qt::green));

    plotFunc(x,v);
    plotFunc(x2,v2, QPen(Qt::red));
    ui->graphWidget->graph(0)->rescaleAxes();
    //ui->graphWidget->graph(1)->rescaleAxes(true);
    ui->graphWidget->replot();
}
